﻿namespace MSSqlSchemaDoc.UI.CommandLine
{
    using System;
    using System.Globalization;
    using System.IO;
    using System.Reflection;

    using MSSqlSchemaDoc.Core;
    using MSSqlSchemaDoc.Core.DataStructures;
    using MSSqlSchemaDoc.UI.CommandLine.Configuration;
    using MSSqlSchemaDoc.Xslt;

    /// <summary>
    /// This is the entry point for the shell of the application.
    /// </summary>
    public static class Program
    {
        /// <summary>
        /// This method gets invoked when running the application from the command line.
        /// </summary>
        /// <param name="args">The command line arguments.</param>
        private static void Main(string[] args)
        {
            bool createHtml = true;
            bool generateXml = true;

            if (args.Length > 0)
            {
                if (args[0] == "/?")
                {
                    WriteLine(Console.Out, "Run the application without any options to generate the Xml and Html report files.", new string[] { });
                    WriteLine(Console.Out, "Use the option \"-nohtml\" to skip generation of the Html reports.", new string[] { });
                    WriteLine(Console.Out, "Use the option \"-noxml\" to skip the generation of the Xml files.", new string[] { });

                    return;
                }
                else
                {
                    foreach (string arg in args)
                    {
                        if (string.Compare(arg, "-nohtml", true) == 0)
                        {
                            createHtml = false;
                            continue;
                        }

                        if (string.Compare(arg, "-noxml", true) == 0)
                        {
                            generateXml = false;
                        }
                    }
                }
            }

            // Read the configuration file
            ConfigurationConnectionSettingCollection confs =
                Properties.Settings.Default.Connections;

            TextWriter w = Console.Out;
            foreach (ConfigurationConnectionSetting setting in confs)
            {
                if (setting.Enabled)
                {
                    DirectoryInfo scriptingFolder = new DirectoryInfo(setting.ScriptingDestinationFolder);
                    DirectoryInfo documentationFolder = new DirectoryInfo(setting.DocumentationDestinationFolder);
                    DirectoryInfo documentationSource = new DirectoryInfo(setting.DocumentationSourceFolder);

                    DirectoryInfo htmlReportFolder = null;
                    if (!string.IsNullOrEmpty(setting.HtmlReportDestinationFolder))
                    {
                        htmlReportFolder = new DirectoryInfo(setting.HtmlReportDestinationFolder);
                    }

                    WriteLine(w, "[Getting scripting manager for connection \"{0}\"]", setting.FriendlyName);

                    if (generateXml)
                    {
                        GenerateXmlFiles(
                            setting.FriendlyName,
                            setting.ProviderAssembly,
                            setting.ProviderType,
                            documentationSource,
                            scriptingFolder,
                            documentationFolder,
                            setting.ConnectionString,
                            w);
                    }

                    if (createHtml && htmlReportFolder != null)
                    {
                        GenerateHtmlReport(
                            setting.FriendlyName,
                            scriptingFolder,
                            documentationFolder,
                            htmlReportFolder,
                            w);
                    }
                }
            }
        }

        /// <summary>
        /// Generates the Xml files.
        /// </summary>
        /// <param name="friendlyName">The friendly name of the connection.</param>
        /// <param name="providerAssembly">The name of the assembly containing the provider object.</param>
        /// <param name="providerType">The name of the provider object.</param>
        /// <param name="documentationSource">The source of previous documentation Xml files.</param>
        /// <param name="scriptingFolder">The destination for the Xml schema files.</param>
        /// <param name="documentationFolder">The destination for the Xml documentation files.</param>
        /// <param name="connectionString">The connection string.</param>
        /// <param name="statusStream">The stream to which status messages should be written.</param>
        private static void GenerateXmlFiles(
            string friendlyName,
            string providerAssembly,
            string providerType,
            DirectoryInfo documentationSource,
            DirectoryInfo scriptingFolder,
            DirectoryInfo documentationFolder,
            string connectionString,
            TextWriter statusStream)
        {
            IScriptingManager sm = GetScriptingManager(providerAssembly, providerType);
            sm.DocumentationSourceFolder = documentationSource;
            sm.IncludeDocumentationObjects = true;

            // Subscribe to events
            SubscribeToEvents(sm, statusStream);

            // Script the connection
            WriteLine(statusStream, "[\"{0}\" - SCRIPTING CONNECTION]", friendlyName);

            sm.ScriptToFile(
                connectionString,
                scriptingFolder,
                true,
                documentationFolder);

            WriteLine(statusStream, "[\"{0}\" - SCRIPTING CONNECTION COMPLETE]", friendlyName);
            WriteLine(statusStream, string.Empty);
        }

        /// <summary>
        /// Generates the Html report files.
        /// </summary>
        /// <param name="friendlyName">The friendly name of the connection.</param>
        /// <param name="scriptingFolder">The folder containing the Xml structure schema files.</param>
        /// <param name="documentationFolder">The folder containing the Xml documentation files.</param>
        /// <param name="htmlReportFolder">The destination folder for the Html report files.</param>
        /// <param name="statusStream">The stream to which status messages should be written.</param>
        private static void GenerateHtmlReport(
            string friendlyName,
            DirectoryInfo scriptingFolder,
            DirectoryInfo documentationFolder,
            DirectoryInfo htmlReportFolder,
            TextWriter statusStream)
        {
            WriteLine(statusStream, "[\"{0}\" - CREATING HTML REPORT]", friendlyName);

            ReportGenerator.GenerateReport(scriptingFolder, documentationFolder, htmlReportFolder, statusStream, friendlyName);

            WriteLine(statusStream, "[\"{0}\" - CREATING HTML REPORT COMPLETE]", friendlyName);
        }

        /// <summary>
        /// A helper method to write text to a specified text writer object.
        /// </summary>
        /// <param name="writer">An instance of a text writer object.</param>
        /// <param name="text">A composite format string.</param>
        /// <param name="values">A string array containing zero or more strings to format.</param>
        private static void WriteLine(TextWriter writer, string text, params string[] values)
        {
            writer.WriteLine(
                String.Format(
                    CultureInfo.CurrentUICulture,
                    text,
                    values));
        }

        /// <summary>
        /// Constructs the appropriate scripting manager object and sets default options.
        /// </summary>
        /// <param name="assemblyName">The name of the assembly to construct.</param>
        /// <param name="providerTypeName">The name of the type of the provider.</param>
        /// <returns>An instance of a scripting manager object.</returns>
        private static IScriptingManager GetScriptingManager(string assemblyName, string providerTypeName)
        {
            // Reflect to load the proper type
            Assembly providerAssembly = Assembly.LoadFile(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), assemblyName));
            Type providerType = providerAssembly.GetType(providerTypeName, true, true);

            // Attempt to instantiate the type
            IScriptingManager sm = (IScriptingManager)providerType.GetConstructor(new Type[] { }).Invoke(new object[] { });
            sm.ValidateXml = true;

            return sm;
        }

        /// <summary>
        /// A helper method to subscribe to the appropriate events on a scripting manager object.
        /// </summary>
        /// <param name="scriptingManager">An instance of a scripting manager object.</param>
        /// <param name="textWriter">The text writer object to write events to.</param>
        private static void SubscribeToEvents(IScriptingManager scriptingManager, TextWriter textWriter)
        {
            scriptingManager.BeginScriptingNewObjectType += delegate(object sender, SimpleEventArgs<string> e)
            {
                WriteLine(textWriter, string.Empty);
                WriteLine(textWriter, "[\"{0}\" - SCRIPTING]", e.Value);
            };

            scriptingManager.CompleteScriptingNewObjectType += delegate(object sender, SimpleEventArgs<string> e)
            {
                WriteLine(textWriter, "[\"{0}\" - SCRIPTING COMPLETE]", e.Value);
                WriteLine(textWriter, string.Empty);
            };

            scriptingManager.BeginScriptingObject += delegate(object sender, SimpleEventArgs<string> e)
            {
                WriteLine(textWriter, "[\"{0}\"]", e.Value);
            };
        }
    }
}
